{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Nonnegative matrix factorization\n",
    "\n",
    "A derivative work by Judson Wilson, 6/2/2014.    \n",
    "Adapted from the CVX example of the same name, by Argyris Zymnis, Joelle Skaf and Stephen Boyd\n",
    "\n",
    "## Introduction\n",
    "\n",
    "We are given a matrix $A \\in \\mathbf{\\mbox{R}}^{m \\times n}$ and are interested in solving the problem:\n",
    "    \\begin{array}{ll}\n",
    "    \\mbox{minimize}   & \\| A - YX \\|_F \\\\\n",
    "    \\mbox{subject to} & Y \\succeq 0 \\\\\n",
    "                      & X \\succeq 0,\n",
    "    \\end{array}\n",
    "where $Y \\in \\mathbf{\\mbox{R}}^{m \\times k}$ and $X \\in \\mathbf{\\mbox{R}}^{k \\times n}$.\n",
    "\n",
    "This example generates a random matrix $A$ and obtains an\n",
    "*approximate* solution to the above problem by first generating\n",
    "a random initial guess for $Y$ and then alternatively minimizing\n",
    "over $X$ and $Y$ for a fixed number of iterations.\n",
    "\n",
    "## Generate problem data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cvxpy as cp\n",
    "import numpy as np\n",
    "\n",
    "# Ensure repeatably random problem data.\n",
    "np.random.seed(0)\n",
    "\n",
    "# Generate random data matrix A.\n",
    "m = 10\n",
    "n = 10\n",
    "k = 5\n",
    "A = np.random.rand(m, k).dot(np.random.rand(k, n))\n",
    "\n",
    "# Initialize Y randomly.\n",
    "Y_init = np.random.rand(m, k)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Perform alternating minimization"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Iteration 1, residual norm 2.766300564135502\n",
      "Iteration 2, residual norm 0.5840356930600721\n",
      "Iteration 3, residual norm 0.3356679970549085\n",
      "Iteration 4, residual norm 0.18670276027770083\n",
      "Iteration 5, residual norm 0.12819921698143966\n",
      "Iteration 6, residual norm 0.09295501592922492\n",
      "Iteration 7, residual norm 0.06766021043574907\n",
      "Iteration 8, residual norm 0.04958204907945361\n",
      "Iteration 9, residual norm 0.03897402158866238\n",
      "Iteration 10, residual norm 0.02979328283505179\n",
      "Iteration 11, residual norm 0.022938564327729952\n",
      "Iteration 12, residual norm 0.021943924920767337\n",
      "Iteration 13, residual norm 0.01810297853945281\n",
      "Iteration 14, residual norm 0.014551161988556204\n",
      "Iteration 15, residual norm 0.014039687334395924\n",
      "Iteration 16, residual norm 0.009354606824469416\n",
      "Iteration 17, residual norm 0.008643141637584189\n",
      "Iteration 18, residual norm 0.007278100007476402\n",
      "Iteration 19, residual norm 0.008486679700021057\n",
      "Iteration 20, residual norm 0.008827511916396866\n",
      "Iteration 21, residual norm 0.008396764193205366\n",
      "Iteration 22, residual norm 0.005265185332845983\n",
      "Iteration 23, residual norm 0.006931929503816392\n",
      "Iteration 24, residual norm 0.007356156596477946\n",
      "Iteration 25, residual norm 0.0039053948996930054\n",
      "Iteration 26, residual norm 0.003989885269615319\n",
      "Iteration 27, residual norm 0.002920361405226024\n",
      "Iteration 28, residual norm 0.007779246694466739\n",
      "Iteration 29, residual norm 0.007339011292898449\n",
      "Iteration 30, residual norm 0.005008539285258121\n"
     ]
    }
   ],
   "source": [
    "# Ensure same initial random Y, rather than generate new one\n",
    "# when executing this cell.\n",
    "Y = Y_init \n",
    "\n",
    "# Perform alternating minimization.\n",
    "MAX_ITERS = 30\n",
    "residual = np.zeros(MAX_ITERS)\n",
    "for iter_num in range(1, 1+MAX_ITERS):\n",
    "    # At the beginning of an iteration, X and Y are NumPy\n",
    "    # array types, NOT CVXPY variables.\n",
    "\n",
    "    # For odd iterations, treat Y constant, optimize over X.\n",
    "    if iter_num % 2 == 1:\n",
    "        X = cp.Variable(shape=(k, n))\n",
    "        constraint = [X >= 0]\n",
    "    # For even iterations, treat X constant, optimize over Y.\n",
    "    else:\n",
    "        Y = cp.Variable(shape=(m, k))\n",
    "        constraint = [Y >= 0]\n",
    "    \n",
    "    # Solve the problem.\n",
    "    # increase max iters otherwise, a few iterations are \"OPTIMAL_INACCURATE\"\n",
    "    # (eg a few of the entries in X or Y are negative beyond standard tolerances)\n",
    "    obj = cp.Minimize(cp.norm(A - Y*X, 'fro'))\n",
    "    prob = cp.Problem(obj, constraint)\n",
    "    prob.solve(solver=cp.SCS, max_iters=10000)\n",
    "\n",
    "    if prob.status != cp.OPTIMAL:\n",
    "        raise Exception(\"Solver did not converge!\")\n",
    "    \n",
    "    print('Iteration {}, residual norm {}'.format(iter_num, prob.value))\n",
    "    residual[iter_num-1] = prob.value\n",
    "\n",
    "    # Convert variable to NumPy array constant for next iteration.\n",
    "    if iter_num % 2 == 1:\n",
    "        X = X.value\n",
    "    else:\n",
    "        Y = Y.value"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Output results"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Original matrix:\n",
      "[[1.323426   1.11061189 1.69137835 1.20020115 1.13216889 0.5980743\n",
      "  1.64965406 0.340611   1.69871738 0.78278448]\n",
      " [1.73721109 1.40464204 1.90898877 1.60774132 1.53717253 0.62647405\n",
      "  1.76242265 0.41151492 1.8048194  1.20313124]\n",
      " [1.4071438  1.10269406 1.75323063 1.18928983 1.23428169 0.60364688\n",
      "  1.63792853 0.40855006 1.57257432 1.17227344]\n",
      " [1.3905141  1.33367163 1.07723947 1.67735654 1.33039096 0.42003169\n",
      "  1.22641711 0.21470465 1.47350799 0.84931787]\n",
      " [1.42153652 1.13598552 2.00816457 1.11463462 1.17914429 0.69942578\n",
      "  1.90353699 0.45664487 1.81023916 1.09668578]\n",
      " [1.60813803 1.23214532 1.73741086 1.3148874  1.27589039 0.40755835\n",
      "  1.31904948 0.3469129  1.34256526 0.76924618]\n",
      " [0.90607895 0.6632877  1.25412229 0.81696721 0.87218892 0.50032884\n",
      "  1.245879   0.25079329 1.25017792 0.72155621]\n",
      " [1.5691922  1.47359672 1.76518996 1.66268312 1.43746574 0.72486628\n",
      "  1.97409333 0.39239642 2.09234807 1.16325748]\n",
      " [1.18723548 1.00282008 1.41532595 1.03836298 0.90382914 0.38460446\n",
      "  1.213473   0.23641422 1.32784402 0.27179726]\n",
      " [0.75789915 0.75119989 0.99502166 0.65444815 0.56073096 0.341146\n",
      "  1.02555143 0.24273668 1.01035919 0.49427978]]\n",
      "Left factor Y:\n",
      "[[ 7.56475742e-01  3.42102372e-01  8.40426641e-01  7.02845111e-01\n",
      "   4.38002833e-03]\n",
      " [ 6.36189366e-01  8.27831861e-01  5.28165827e-01  5.60609403e-01\n",
      "   3.34595403e-02]\n",
      " [ 5.54834858e-01  6.37954560e-01  8.01726231e-01  1.96879041e-01\n",
      "   3.74736667e-02]\n",
      " [ 2.72955779e-01  9.53749151e-01  6.14934798e-02  9.81276972e-01\n",
      "  -4.26647247e-05]\n",
      " [ 7.93952558e-01  3.50946872e-01  1.18853643e+00  3.85961318e-01\n",
      "   2.96701863e-02]\n",
      " [ 7.26183347e-01  4.41639937e-01  2.71711699e-03  7.33393633e-01\n",
      "   4.55176129e-02]\n",
      " [ 4.89263105e-01  4.20725095e-01  7.56036398e-01  6.24033457e-02\n",
      "  -5.38302416e-04]\n",
      " [ 6.09810836e-01  7.55780427e-01  1.03636918e+00  9.08549910e-01\n",
      "   1.91844947e-03]\n",
      " [ 8.31578328e-01  8.75528332e-05  2.93543168e-01  1.10037225e+00\n",
      "  -2.65884776e-04]\n",
      " [ 4.26650967e-01  5.53761974e-02  6.52855369e-01  6.43132832e-01\n",
      "   1.47569255e-02]]\n",
      "Right factor X:\n",
      "[[ 1.07015116e+00  4.25961964e-01  1.59511553e+00  6.26808607e-01\n",
      "   8.98124301e-01  3.62801718e-01  9.53757673e-01  1.88661317e-01\n",
      "   9.64559055e-01  1.43675625e-01]\n",
      " [ 8.72908811e-01  7.03553498e-01  6.45229205e-01  1.10121868e+00\n",
      "   9.93621271e-01  3.12383803e-01  7.45085312e-01  1.25155585e-01\n",
      "   8.84272390e-01  7.94988511e-01]\n",
      " [ 1.41086863e-04  1.70049131e-01  2.73427259e-01  2.50933223e-02\n",
      "   8.38007474e-03  2.51575697e-01  5.99473425e-01  1.39362252e-01\n",
      "   5.06840502e-01  4.22844259e-01]\n",
      " [ 2.70906925e-01  5.46340550e-01  1.04256418e-02  4.63290841e-01\n",
      "   1.39889787e-01  7.65220031e-03  2.22742919e-01  3.60875098e-02\n",
      "   3.41601146e-01  2.72448408e-02]\n",
      " [ 5.44108256e+00  4.62667224e+00  6.26354249e+00  7.23656013e-01\n",
      "   1.81220987e+00 -2.57729003e-07  2.90739234e+00  2.81123997e+00\n",
      "  -2.15606388e-06  6.43189790e+00]]\n",
      "Residual A - Y * X:\n",
      "[[ 9.02157264e-04  5.23117764e-04 -5.79950842e-04 -5.74317402e-04\n",
      "  -4.61768644e-04 -5.28680186e-05  1.62394448e-04  2.76277321e-04\n",
      "   4.85227596e-04 -5.60481823e-04]\n",
      " [-2.33027425e-04  3.21455250e-04  2.17040399e-04  1.56606195e-04\n",
      "  -2.41256203e-04 -1.01386736e-04  7.36342995e-05 -1.73587325e-05\n",
      "  -5.22429324e-05 -2.04432888e-04]\n",
      " [-8.35846517e-04  2.46121871e-04  5.93720663e-04  5.38806481e-04\n",
      "  -8.42363429e-05 -1.36215640e-04  2.31633730e-06 -1.52108618e-04\n",
      "  -3.23620331e-04 -5.42078084e-06]\n",
      " [ 2.62860853e-04  1.83780003e-05 -3.20542830e-04 -1.49712163e-04\n",
      "  -1.31334078e-04  8.78805144e-05  1.46798183e-04 -2.03546983e-05\n",
      "   4.79256197e-04 -5.81320754e-04]\n",
      " [-6.22557723e-04  6.31892711e-04  4.34719938e-04  4.01388769e-04\n",
      "  -3.52745774e-04 -2.12014739e-04  8.42548761e-05 -4.17321003e-05\n",
      "  -1.50760383e-04 -3.01455643e-04]\n",
      " [-8.46202248e-04  3.61714835e-04  6.15005890e-04  5.85452470e-04\n",
      "  -2.39872783e-04 -1.59000367e-04  6.24749082e-05 -1.69461803e-04\n",
      "  -3.16622183e-04 -8.20910778e-05]\n",
      " [ 1.15561552e-03 -1.28864368e-03 -1.77288000e-03 -5.10264071e-04\n",
      "   6.38713553e-04  7.17730381e-04  2.05892579e-04 -2.69449092e-04\n",
      "   1.71225020e-03 -1.13410340e-03]\n",
      " [ 1.57913703e-04  6.21168134e-04 -4.04695033e-05 -1.48187018e-04\n",
      "  -4.38037868e-04 -1.45409129e-04  1.34145488e-04  1.47289692e-04\n",
      "   1.98184939e-04 -5.09549810e-04]\n",
      " [ 5.51365483e-04 -1.32683206e-03 -1.26345269e-03  6.01647636e-05\n",
      "   9.72529426e-04  6.10472383e-04 -1.48674297e-05 -3.54468161e-04\n",
      "   9.92202367e-04 -1.42249517e-04]\n",
      " [-1.63514531e-03 -1.59800828e-04  1.08957766e-03  1.01954949e-03\n",
      "   3.41048252e-04 -1.06257705e-04 -1.57094132e-04 -3.64204427e-04\n",
      "  -7.26930797e-04  4.63755883e-04]]\n",
      "Residual after 30 iterations: 0.005008539285258121\n"
     ]
    }
   ],
   "source": [
    "#\n",
    "# Plot residuals.\n",
    "#\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Show plot inline in ipython.\n",
    "%matplotlib inline\n",
    "\n",
    "# Set plot properties.\n",
    "plt.rc('text', usetex=True)\n",
    "plt.rc('font', family='serif')\n",
    "font = {'weight' : 'normal',\n",
    "        'size'   : 16}\n",
    "plt.rc('font', **font)\n",
    "\n",
    "# Create the plot.\n",
    "plt.plot(residual)\n",
    "plt.xlabel('Iteration Number')\n",
    "plt.ylabel('Residual Norm')\n",
    "plt.show()\n",
    "\n",
    "#\n",
    "# Print results.\n",
    "#\n",
    "print('Original matrix:')\n",
    "print(A)\n",
    "print('Left factor Y:')\n",
    "print(Y)\n",
    "print('Right factor X:')\n",
    "print(X)\n",
    "print('Residual A - Y * X:')\n",
    "print(A - Y.dot(X))\n",
    "print('Residual after {} iterations: {}'.format(iter_num, prob.value))\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
